package cn.sc.jedis;

import cn.sc.jedis.serialization.DefaultSerialization;
import cn.sc.jedis.serialization.Serialization;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.*;
import java.util.function.Function;

/**
 * @ClassName RedisSingle
 * @Author 谢卓才
 * @Description 单机redis的操作
 * @Date 2022/11/13 9:17
 */
public final class RedisSingle implements RedisCommons {

    private final JedisPool jedisPool;

    private final Serialization serialization;

    public RedisSingle(JedisPool jedisPool, Serialization serialization) {
        this.jedisPool = jedisPool;
        this.serialization = serialization != null ? serialization : new DefaultSerialization();
    }

    @Override
    public <V> V hSet(String key, String item, V value) {
        byte[] newKey = serialization.packString(key);
        if(newKey == null){
            return null;
        }
        byte[] newItems = serialization.packString(item);
        if(newItems == null){
            return null;
        }
        byte[] newValue = serialization.packObject(value);
        byte[] resultValue = (byte[])this.execute(conn->{
            byte[] result = conn.hget(newKey, newItems);
            conn.hset(newKey, newItems, newValue);
            return result;
        });
        return serialization.dismantleObject(resultValue);
    }

    @Override
    public <V> void hmset(String key, Map<String, V> values) {
        byte[] newKey = this.serialization.packString(key);
        if(newKey == null){
            return;
        }
        Map<byte[], byte[]> newValue = this.serialization.packObjectMap(values);
        if(newValue == null){
            return;
        }
        this.execute(conn->conn.hmset(newKey, newValue));
    }

    @Override
    public <V> V hGet(String key, String item) {
        byte[] newKey = serialization.packString(key);
        byte[] newItem = serialization.packString(item);
        if(newKey == null || newItem == null){
            return null;
        }
        byte[] result = (byte[])this.execute(conn -> conn.hget(newKey, newItem));
        if(result != null){
            return serialization.dismantleObject(result);
        }
        return null;
    }

    @Override
    public <V> List<V> hGet(String key, String... items) {
        byte[] newKey = serialization.packString(key);
        byte[][] newItems = serialization.packStringMulti(items);
        if(newKey == null || newItems == null){
            return null;
        }
        List<byte[]> result = (List<byte[]>)this.execute(conn -> conn.hmget(newKey,newItems));
        return serialization.dismantleMulti(result);
    }

    @Override
    public <V> Map<String, V> hGetAll(String key) {
        byte[] newKey = serialization.packString(key);
        if(newKey == null){
            return null;
        }
        Map<byte[], byte[]> execute = (Map<byte[], byte[]>)this.execute(conn -> conn.hgetAll(newKey));
        return serialization.dismantleMap(execute);
    }

    @Override
    public <V> List<V> hValues(String key) {
        byte[] newKey = serialization.packString(key);
        if(newKey == null){
            return null;
        }
        List<byte[]> result = (List<byte[]>)this.execute(conn -> conn.hvals(newKey));
        return serialization.dismantleMulti(result);
    }

    @Override
    public Set<String> hKeys(String key){
        byte[] newKey = serialization.packString(key);
        if(newKey == null){
            return null;
        }
        Set<byte[]> execute = (Set<byte[]>)this.execute(conn -> conn.hkeys(newKey));
        return serialization.dismantleStringMulti(execute);
    }

    @Override
    public Long del(String key) {
        byte[] newKey = serialization.packString(key);
        if(newKey == null){
            return 0L;
        }
        return (Long)this.execute(conn->conn.del(newKey));
    }

    @Override
    public Long hSize(String key) {
        byte[] newKey = serialization.packString(key);
        if(newKey == null){
            return 0L;
        }
        return (Long) this.execute(conn->conn.hlen(newKey));
    }

    @Override
    public Long hDel(String key, String... items) {
        byte[] newKey = serialization.packString(key);
        byte[][] newItems = serialization.packStringMulti(items);
        if(newKey == null || newItems == null){
            return 0L;
        }
        return (Long) this.execute(conn->conn.hdel(newKey,newItems));
    }

    public <V> Object[] hSetAndSize(String key, String item,V value) {
        byte[] newKey = serialization.packString(key);
        byte[] newItem = serialization.packString(item);
        if(newKey == null || newItem == null){
            return new Object[2];
        }
        byte[] newValue = serialization.packObject(value);
        Object[] result = (Object[])this.execute(conn -> {
            byte[] hget = conn.hget(newKey, newItem);
            conn.hset(newKey, newItem, newValue);
            Long hlen = conn.hlen(newKey);
            Object[] res = {serialization.dismantleObject(hget),hlen};
            return res;
        });
        return result;
    }

    private Object execute(Function<Jedis,Object> function){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Object result = function.apply(jedis);
            return result;
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            if(jedis!=null){
                jedis.close();
            }
        }
        return null;
    }

}